package com.kbs.im.common.protocol.codec;

import com.kbs.im.common.protocol.packet.BasePacket;
import com.kbs.im.common.protocol.packet.request.DeviceRegisterRequestPacket;
import com.kbs.im.common.protocol.packet.request.HeartbeatRequestPacket;
import com.kbs.im.common.protocol.packet.request.ListAllDeviceRequestPacket;
import com.kbs.im.common.protocol.packet.request.LoginRequestPacket;
import com.kbs.im.common.protocol.packet.response.DeviceRegisterResponsePacket;
import com.kbs.im.common.protocol.packet.response.HeartbeatResponsePacket;
import com.kbs.im.common.protocol.packet.response.ListAllDeviceResponsePacket;
import com.kbs.im.common.protocol.packet.response.LoginResponsePacket;
import com.kbs.im.common.protocol.serialize.Serializer;
import com.kbs.im.common.protocol.serialize.impl.JSONSerializer;
import io.netty.buffer.ByteBuf;

import java.util.HashMap;
import java.util.Map;

import static com.kbs.im.common.protocol.command.Command.*;

/**
 * 自定义的，基于长度域的通信协议
 * <p>
 * 协议描述：
 * 魔数(4字节) | 协议版本(byte 1字节) | 序列化算法标识(byte 1字节) | 业务指令码(int 4字节) | 数据包长度(int 4字节) | 数据包数据
 * <p>
 * 编解码实现
 *
 * @author zhangzhang
 */
public class PacketCodec {

    static final int MAGIC_NUMBER = 0x12345678;
    public static final PacketCodec INSTANCE = new PacketCodec();

    private final Map<Integer, Class<? extends BasePacket>> packetTypeMap;
    private final Map<Byte, Serializer> serializerMap;


    private PacketCodec() {
        //请求响应消息实体map
        packetTypeMap = new HashMap<>();
        packetTypeMap.put(USER_LOGIN_REQUEST.getCode(), LoginRequestPacket.class);
        packetTypeMap.put(USER_LOGIN_RESPONSE.getCode(), LoginResponsePacket.class);
        packetTypeMap.put(DEVICE_REGISTER_REQUEST.getCode(), DeviceRegisterRequestPacket.class);
        packetTypeMap.put(DEVICE_REGISTER_RESPONSE.getCode(), DeviceRegisterResponsePacket.class);
        packetTypeMap.put(HEARTBEAT_REQUEST.getCode(), HeartbeatRequestPacket.class);
        packetTypeMap.put(HEARTBEAT_RESPONSE.getCode(), HeartbeatResponsePacket.class);
        packetTypeMap.put(LIST_ALL_DEVICE_REQUEST.getCode(), ListAllDeviceRequestPacket.class);
        packetTypeMap.put(LIST_ALL_DEVICE_RESPONSE.getCode(), ListAllDeviceResponsePacket.class);
        //序列化算法map
        serializerMap = new HashMap<>();
        Serializer serializer = new JSONSerializer();
        serializerMap.put(serializer.getSerializerAlgorithm(), serializer);
    }

    /**
     * 消息编码
     *
     * @param byteBuf
     * @param packet
     */
    void encode(ByteBuf byteBuf, BasePacket packet) {
        // 1. 序列化 java 对象
        byte[] bytes = Serializer.DEFAULT.serialize(packet);
        // 2. 实际编码过程
        byteBuf.writeInt(MAGIC_NUMBER);
        byteBuf.writeByte(packet.getVersion());
        byteBuf.writeByte(Serializer.DEFAULT.getSerializerAlgorithm());
        byteBuf.writeInt(packet.getCommand());
        byteBuf.writeInt(bytes.length);
        byteBuf.writeBytes(bytes);
    }


    /**
     * 消息解码
     *
     * @param byteBuf
     * @return
     */
    BasePacket decode(ByteBuf byteBuf) {
        // 已有Spliter做魔数校验，此处跳过 magic number
        byteBuf.skipBytes(4);
        // 跳过版本号
        byteBuf.skipBytes(1);
        // 序列化算法
        byte serializeAlgorithm = byteBuf.readByte();
        // 指令
        int command = byteBuf.readInt();
        // 数据包长度
        int length = byteBuf.readInt();
        byte[] bytes = new byte[length];
        byteBuf.readBytes(bytes);
        Class<? extends BasePacket> requestType = getRequestType(command);
        Serializer serializer = getSerializer(serializeAlgorithm);
        if (requestType == null) {
            System.out.println("无法根据指令码[" + command + "]获取到对应的反序列化实体！");
        }
        if (requestType != null && serializer != null) {
            return serializer.deserialize(requestType, bytes);
        }
        return null;
    }

    /**
     * 获取序列化方式
     *
     * @param serializeAlgorithm
     * @return
     */
    private Serializer getSerializer(byte serializeAlgorithm) {
        return serializerMap.get(serializeAlgorithm);
    }

    /**
     * 查询对应请求业务类型
     *
     * @param command
     * @return
     */
    private Class<? extends BasePacket> getRequestType(int command) {
        return packetTypeMap.get(command);
    }
}
